#include "CvGameCoreDLL.h"
#include "PlanetValueEventHandler.h"

struct PlanetEventRegistrationStruct
{
	// -------------------
	// Set when registered
	// -------------------
	std::string m_szRegistrationName;		// The name of the event
	int m_iPlanetValue;						// The planet value that triggers this event
	bool m_bAscending;						// The event is triggered if the planet value is hit while ascending.
	bool m_bDescending;						// The event is triggered if the planet value is hit while descending
	bool m_bRepeat;							// The event will be triggered more than once.

	// -----------------------------
	// Used internally by the system
	// -----------------------------
	int m_iTotalCount;						// The total number of times that this event has run.
	int m_iTotalNewCount;						// The total number of times that the event has run without being cleared.
};


PlanetValueEventHandler::PlanetValueEventHandler()
{
}

PlanetValueEventHandler::~PlanetValueEventHandler()
{
}

void PlanetValueEventHandler::insertNewRegistrationStruct(std::string szRegistrationName, int iPlanetValue, bool bAscending, bool bDescending, bool bRepeat, int iTotalCount, int iTotalNewCount)
{
	// Create the new structure to hold the data
	PlanetEventRegistrationStruct eventStruct;
	eventStruct.m_szRegistrationName = szRegistrationName;
	eventStruct.m_iPlanetValue = iPlanetValue;
	eventStruct.m_bAscending = bAscending;
	eventStruct.m_bDescending = bDescending;
	eventStruct.m_bRepeat = bRepeat;

	eventStruct.m_iTotalCount = iTotalCount;
	eventStruct.m_iTotalNewCount = iTotalNewCount;

	// Add the registration structure to a the map.
	m_registrationMap.insert(std::pair<int, PlanetEventRegistrationStruct>(iPlanetValue, eventStruct));

	// Add the registration name's lookup to a lookup map
	m_lookup.insert(std::pair<std::string, int>(szRegistrationName, iPlanetValue));
}



void PlanetValueEventHandler::registerPlanetEvent(std::string szRegistrationName, int iPlanetValue, bool bAscending, bool bDescending, bool bRepeat)
{
	if (m_lookup.count(szRegistrationName) > 0)
	{
		throw "Name already in use";
	}

	insertNewRegistrationStruct(szRegistrationName, iPlanetValue, bAscending, bDescending, bRepeat);
}

void PlanetValueEventHandler::unregisterPlanetEvent(std::string szRegistrationName)
{
	PlanetValueRegistrationIterator iterator;

	if (m_lookup.count(szRegistrationName) == 0)
	{
		throw "Name not found to unregister";
	}

	iterator = getIteratorForRegistration(szRegistrationName);

	if (iterator != NULL)
	{
		m_registrationMap.erase(iterator);
		m_lookup.erase(szRegistrationName);
	}
}

void PlanetValueEventHandler::unregisterAllPlanetEvents()
{
	m_registrationMap.clear();
	m_lookup.clear();
}

PlanetValueRegistrationIterator PlanetValueEventHandler::getIteratorForRegistration(std::string szRegistrationName)
{
	PlanetValueRegistrationIterator lowerBound, upperBound, iterator;

	if (m_lookup.count(szRegistrationName) == 0)
	{
		throw "Name not found";
	}
	
	// Find the range that it will be hiding in.
	int iPlanetValue = m_lookup.find(szRegistrationName)->second;

	// Iterate through all the events for that planet value's range. When we find what we're looking for, return it.
	lowerBound = m_registrationMap.lower_bound(iPlanetValue);
	upperBound = m_registrationMap.upper_bound(iPlanetValue);
	for (iterator = lowerBound; iterator != upperBound; ++iterator)
	{
		if (iterator->second.m_szRegistrationName == szRegistrationName)
		{
			break;
		}
	}

	if (iterator == upperBound)
	{
		return NULL;
	}

	return iterator;
}

void PlanetValueEventHandler::handlePlanetValueChange(int iOld, int iNew)
{
	PlanetValueRegistrationIterator lowerBound, upperBound, iterator;
	bool bAscending;
	bool bEventPotential;
	int iLowestPassed;
	int iHighestPassed;
	
	FAssertMsg(iOld != iNew, "Planet value change is being handled without any change occuring");

	// Get what planet values should be "hit" due to this change.
	bAscending = iOld < iNew;
	if (bAscending)
	{
		iLowestPassed = iOld + 1;
		iHighestPassed = iNew;
	}
	else
	{
		iLowestPassed = iNew;
		iHighestPassed = iOld - 1;
	}

	lowerBound = m_registrationMap.lower_bound(iLowestPassed);
	upperBound = m_registrationMap.upper_bound(iHighestPassed);

	// Loop through each registered event and check if it should be triggered to run.
	// Just increment the run count so that when players check later, the count
	// shows that they should run.
	for (iterator = lowerBound; iterator != upperBound; ++iterator)
	{
		// Check if the ascending/descending values allow this event to happen.
		bEventPotential = false;
		if (bAscending)
		{
			if (iterator->second.m_bAscending)
			{
				bEventPotential = true;
			}
		}
		else
		{
			if (iterator->second.m_bDescending)
			{
				bEventPotential = true;
			}
		}

		// Check that this is a repeatable event, or at least if it's the first time that the event happens.
		if (bEventPotential)
		{
			if (iterator->second.m_bRepeat || iterator->second.m_iTotalCount == 0)
			{
				iterator->second.m_iTotalCount++;
				iterator->second.m_iTotalNewCount++;
			}
		}
	}	
}

int PlanetValueEventHandler::getEventNewHitCount(std::string szRegistrationName)
{
	PlanetValueRegistrationIterator iterator;
	int iNewHitCount;

	iterator = getIteratorForRegistration(szRegistrationName);

	if (iterator != NULL)
	{
		iNewHitCount = iterator->second.m_iTotalNewCount;
	}

	return iNewHitCount;
}

void PlanetValueEventHandler::changeEventNewHitCount(std::string szRegistrationName, int iChange)
{
	PlanetValueRegistrationIterator iterator;

	iterator = getIteratorForRegistration(szRegistrationName);

	if (iterator != NULL)
	{
		if (iterator->second.m_iTotalNewCount + iChange < 0)
		{
			throw "Tried changing to a number less than zero";
		}
		iterator->second.m_iTotalNewCount += iChange;
	}
}

void PlanetValueEventHandler::setEventNewHitCount(std::string szRegistrationName, int iValue)
{
	if (iValue < 0)
	{
		throw "Tried changing to a number less than zero";
	}

	PlanetValueRegistrationIterator iterator;

	iterator = getIteratorForRegistration(szRegistrationName);

	if (iterator != NULL)
	{
		iterator->second.m_iTotalNewCount = iValue;
	}
}

void PlanetValueEventHandler::read(FDataStreamBase* pStream)
{
	std::string szRegistrationName;
	int iPlanetValue;
	bool bAscending;
	bool bDescending;
	bool bRepeat;
	int iTotalCount;
	int iTotalNewCount;
	
	int iCount;
	int iI;
	std::string szTemp;

	unregisterAllPlanetEvents();

	

	pStream->Read(&iCount);

	for (iI = 0; iI < iCount; ++iI)
	{
		pStream->ReadString(szRegistrationName);
		pStream->Read(&iPlanetValue);
		pStream->Read(&bAscending);
		pStream->Read(&bDescending);
		pStream->Read(&bRepeat);

		pStream->Read(&iTotalCount);
		pStream->Read(&iTotalNewCount);

		insertNewRegistrationStruct(szRegistrationName, iPlanetValue, bAscending, bDescending, bRepeat, iTotalCount, iTotalNewCount);
	}

}

void PlanetValueEventHandler::write(FDataStreamBase* pStream)
{
	PlanetValueRegistrationIterator begin, end, iterator;

	begin = m_registrationMap.begin();
	end = m_registrationMap.end();

	int iCount = 0;

	for (iterator = begin; iterator != end; ++iterator)
	{
		iCount++;
	}

	pStream->Write(iCount);

	for (iterator = begin; iterator != end; ++iterator)
	{
		pStream->WriteString(iterator->second.m_szRegistrationName);
		pStream->Write(iterator->second.m_iPlanetValue);
		pStream->Write(iterator->second.m_bAscending);
		pStream->Write(iterator->second.m_bDescending);
		pStream->Write(iterator->second.m_bRepeat);
		
		pStream->Write(iterator->second.m_iTotalCount);
		pStream->Write(iterator->second.m_iTotalNewCount);
	}
}
		
